<!--
 * @Author: xcl
 * @Date: 2022-04-20 18:05:17
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2022-10-09 09:58:01
 * @Description: 迁徙图
-->
<template>
  <div>
    <cesium-map @fun="fun" :opt="opt" :type="2"></cesium-map>
  </div>
</template>

<script>
import CesiumMap from "@/components/map/CesiumMap.vue";
export default {
  name: "CesiumMigration",
  components: {
    "cesium-map": CesiumMap,
  },
  data() {
    return {
      opt: {
        view: "100.56309029335371,26.706785718748684,1594297.6097542574,351.6253939109711,-75.94750617303563,0.1354463777427236",
        shouldAnimate: true,
      },

      // 飞线数据
      datas: [
        // 重庆 -> 成都
        {
          point1: [106.505118, 29.504284],
          point2: [104.088686, 30.659367],
          h: 200000,
          fragmentShaderSource: `
            varying vec2 v_st;    
            varying float v_width;    
            varying float v_polylineAngle;
            varying vec4 v_positionEC;
            varying vec3 v_normalEC;
            void main()
            {
                vec2 st = v_st;

                float xx = fract(st.s - czm_frameNumber/30.0);
                float r = xx;
                float g = 0.0;
                float b = 0.0;
                float a = xx;

                gl_FragColor = vec4(r,g,b,a);
            }
          `,
        },
        // 成都 -> 广元
        {
          point1: [104.088686, 30.659367],
          point2: [105.826658, 32.435685],
          h: 10000,
          fragmentShaderSource: ` varying vec2 v_st;    
            varying float v_width;    
            varying float v_polylineAngle;
            varying vec4 v_positionEC;
            varying vec3 v_normalEC;
            void main()
            {
                vec2 st = v_st;

                float num = 4.0;
                float xx = fract(st.s*num - czm_frameNumber/60.0);
                float r = xx;
                float g = 0.0;
                float b = 0.0;
                float a = xx;
                if(fract(st.s*num/4.0 - czm_frameNumber/240.0)<0.75){
                    a=0.0;
                }

                gl_FragColor = vec4(r,g,b,a);
            }
          `,
        },
        // 成都 -> 泸州
        {
          point1: [104.088686, 30.659367],
          point2: [105.439521, 28.895318],
          h: 100000,
          fragmentShaderSource: `
            varying vec2 v_st;    
            varying float v_width;    
            varying float v_polylineAngle;
            varying vec4 v_positionEC;
            varying vec3 v_normalEC;
            void main()
            {
                vec2 st = v_st;

                // 七彩渐变飞线，宽度2
                float xx = fract(st.s*2.0 - czm_frameNumber/60.0);
                float r = xx;
                float g = sin(czm_frameNumber/30.0);
                float b = cos(czm_frameNumber/30.0);
                float a = xx;

                gl_FragColor = vec4(r,g,b,a);
            }
          `,
        },
        // 成都 -> 达州
        {
          point1: [104.088686, 30.659367],
          point2: [107.491600, 31.206684],
          h: 200000,
          fragmentShaderSource: `
            varying vec2 v_st;    
            varying float v_width;    
            varying float v_polylineAngle;
            varying vec4 v_positionEC;
            varying vec3 v_normalEC;
            void main()
            {
                vec2 st = v_st;
                
                //卡巴斯基
                float xx = sin(st.s*6.0 -czm_frameNumber/5.0) - cos(st.t*6.0);
                float r = 0.0;
                float g = xx;
                float b = xx;
                float a = xx;

                gl_FragColor = vec4(r,g,b,a);
            }
          `,
        },
        // 拉萨 -> 成都
        {
          point1: [91.087162, 29.647486],
          point2: [104.088686, 30.659367],
          h: 1000000,
          fragmentShaderSource: `
            varying vec2 v_st;    
            varying float v_width;    
            varying float v_polylineAngle;
            varying vec4 v_positionEC;
            varying vec3 v_normalEC;
            void main()
            {
                vec2 st = v_st;
                // 箭头飞线，宽度 8
                float xx = fract(st.s*10.0 + st.t  - czm_frameNumber/60.0);
                if (st.t<0.5) {
                    xx = fract(st.s*10.0 - st.t - czm_frameNumber/60.0);
                }
                float r = 0.0;
                float g = xx;
                float b = xx;
                float a = xx;

                // 飞线边框
                if (st.t>0.8||st.t<0.2) {
                    g = 1.0;
                    b = 1.0;
                    a = 0.4;
                }

                gl_FragColor = vec4(r,g,b,a);
            }
          `,
        },
      ],

      // 点位
      points: [
        {
          isShow: true, // 显隐
          id: 1, // 唯一性
          name: "成都", // label文本
          position: { lng: 104.088686, lat: 30.659367, height: 0 }, // 定位
          properties: {
            // 默认点位图片
            defaultImage: {
              base64: require("../../assets/images/map-normal.png"), // base64
              height: 56, // 高度
              width: 40, // 宽度
            },
            // 激活点位图片
            activeImage: {
              base64: require("../../assets/images/map-select.png"), // base64
              height: 68, // 高度
              width: 48, // 宽度
            },
            // 点位信息窗口
            popupConfig: {
              width: 750, // 宽度
              height: 368, // 高度
            },
            // 额外信息
            extraData: {},
          },
          billboard: null, // billboard的配置
          label: null, //label的配置
        },
        {
          isShow: true,
          id: 2,
          name: "重庆",
          position: { lng: 106.505118, lat: 29.504284, height: 0 },
          properties: {
            defaultImage: {
              base64: require("../../assets/images/map-normal.png"),
              height: 56,
              width: 40,
            },
            activeImage: {
              base64: require("../../assets/images/map-select.png"),
              height: 68,
              width: 48,
            },
            popupConfig: {
              width: 750,
              height: 368,
            },
            apiData: {},
          },
          billboard: null,
          label: null,
        },
        {
          isShow: true,
          id: 3,
          name: "广元",
          position: { lng: 105.826658, lat: 32.435685, height: 0 },
          properties: {
            defaultImage: {
              base64: require("../../assets/images/map-normal.png"),
              height: 56,
              width: 40,
            },
            activeImage: {
              base64: require("../../assets/images/map-select.png"),
              height: 68,
              width: 48,
            },
            popupConfig: {
              width: 750,
              height: 368,
            },
            apiData: {},
          },
          billboard: null,
          label: null,
        },
        {
          isShow: true,
          id: 4,
          name: "泸州",
          position: { lng: 105.439521, lat: 28.895318, height: 0 },
          properties: {
            defaultImage: {
              base64: require("../../assets/images/map-normal.png"),
              height: 56,
              width: 40,
            },
            activeImage: {
              base64: require("../../assets/images/map-select.png"),
              height: 68,
              width: 48,
            },
            popupConfig: {
              width: 750,
              height: 368,
            },
            apiData: {},
          },
          billboard: null,
          label: null,
        },
        {
          isShow: true,
          id: 5,
          name: "达州",
          position: { lng: 107.491600, lat: 31.206684, height: 0 },
          properties: {
            defaultImage: {
              base64: require("../../assets/images/map-normal.png"),
              height: 56,
              width: 40,
            },
            activeImage: {
              base64: require("../../assets/images/map-select.png"),
              height: 68,
              width: 48,
            },
            popupConfig: {
              width: 750,
              height: 368,
            },
            apiData: {},
          },
          billboard: null,
          label: null,
        },
        {
          isShow: true,
          id: 6,
          name: "拉萨",
          position: { lng: 91.087162, lat: 29.647486, height: 0 },
          properties: {
            defaultImage: {
              base64: require("../../assets/images/map-normal.png"),
              height: 56,
              width: 40,
            },
            activeImage: {
              base64: require("../../assets/images/map-select.png"),
              height: 68,
              width: 48,
            },
            popupConfig: {
              width: 750,
              height: 368,
            },
            apiData: {},
          },
          billboard: null,
          label: null,
        },
      ],
    };
  },
  props: {},
  watch: {},
  created() {},
  mounted() {},
  beforeDestroy() {},
  destroyed() {},
  methods: {
    fun(viewer, vm) {
      let self = this;
      this.eventInteraction(viewer);
      this.datas.forEach((element, index) => {
        self.addFlyline(viewer, element, index);
      });
      new CesiumGlobe().addBillboard(viewer, self.points, "Flyline", vm);
      new CesiumGlobe().addLabel(viewer, self.points, "Flyline", vm);
    },

    /**
     * 事件交互（鼠标移动和点击）
     * @param {*} viewer
     */
    eventInteraction(viewer) {
      let self = this;
      let handler;
      if (handler != undefined) {
        ShapeEventHandler.removeInputAction(
          Cesium.ScreenSpaceEventType.MOUSE_MOVE
        );
        ShapeEventHandler.removeInputAction(
          Cesium.ScreenSpaceEventType.LEFT_CLICK
        );
      }
      handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);

      handler.setInputAction(function (event) {
        // console.log("移动事件：", event);
        // var pick = viewer.scene.pick(event.endPosition);
        // console.log(pick)
        // if (Cesium.defined(pick)) {
        // } else {
        // }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      // 点击
      handler.setInputAction(function (event) {
        // console.log("点击事件：", event);
        // var pick = viewer.scene.pick(event.position);
        // console.log(pick)
        // if (Cesium.defined(pick)) {
        // } else {
        // }

        // =======================================================================================
        const pickRay = viewer.camera.getPickRay(event.position);
        var cartesian = viewer.scene.globe.pick(pickRay, viewer.scene);
        // 拾取地形高度/海拔、位置信息、经纬度数据
        if (cartesian !== undefined) {
          var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
          let earthPosition = viewer.camera.pickEllipsoid(
            event.position,
            viewer.scene.globe.ellipsoid
          );
          console.log("位置信息1：", earthPosition);
          var maplnglat =
            viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
          console.log("位置信息2：", maplnglat);
          if (Cesium.defined(earthPosition)) {
            let lon = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6);
            let lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(6);
            console.log("位置信息3 - 经纬度：", lon, lat);
            let height = viewer.scene.globe.getHeight(cartographic).toFixed(3);
            console.log("地形高度/海拔：", height);
          }
        }
        // 拾取相机数据
        if (true) {
          let cheight = viewer.camera.positionCartographic.height.toFixed(2);
          console.log("相机高度数据：", cheight);
          let bestView = {
            position: {
              x: viewer.camera.positionWC.x,
              y: viewer.camera.positionWC.y,
              z: viewer.camera.positionWC.z,
            },
            // 方向 围绕Z轴旋转
            heading: Cesium.Math.toDegrees(viewer.camera.heading).toFixed(2),
            // 倾斜角度 围绕Y轴旋转
            pitch: Cesium.Math.toDegrees(viewer.camera.pitch).toFixed(2),
            // 围绕X轴旋转
            roll: Cesium.Math.toDegrees(viewer.camera.roll).toFixed(2),
          };
          console.log("相机最佳位置数据：", JSON.stringify(bestView));
        }
        // =======================================================================================
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    },

    /**
     * 根据经纬度、高计算飞线数据
     * @param {起点} point1
     * @param {终点} point2
     * @param {高度} h
     */
    computeFlyline(
      point1 = [126.957, 45.547],
      point2 = [120.28429, 31.52853],
      h = 500000
    ) {
      let flyline = getBSRxyz(...point1, ...point2, h);
      return flyline;
      // 将数据转换为cesium polyline positions格式
      function getBSRxyz(x1, y1, x2, y2, h) {
        let arr3d = getBSRPoints(x1, y1, x2, y2, h);
        let arrAll = [];
        for (let ite of arr3d) {
          arrAll.push(ite[0]);
          arrAll.push(ite[1]);
          arrAll.push(ite[2]);
        }
        return Cesium.Cartesian3.fromDegreesArrayHeights(arrAll);
      }
      function getBSRPoints(x1, y1, x2, y2, h) {
        let point1 = [y1, 0];
        let point2 = [(y2 + y1) / 2, h];
        let point3 = [y2, 0];
        let arr = getBSR(point1, point2, point3);
        let arr3d = [];
        for (let i = 0; i < arr.length; i++) {
          let x = ((x2 - x1) * (arr[i][0] - y1)) / (y2 - y1) + x1;
          arr3d.push([x, arr[i][0], arr[i][1]]);
        }
        return arr3d;
      }
      // 生成贝塞尔曲线
      function getBSR(point1, point2, point3) {
        var ps = [
          { x: point1[0], y: point1[1] },
          { x: point2[0], y: point2[1] },
          { x: point3[0], y: point3[1] },
        ];
        // 100 每条线由100个点组成
        let guijipoints = CreateBezierPoints(ps, 100);
        return guijipoints;
      }
      // 贝赛尔曲线算法
      // 参数：
      // anchorpoints: [{ x: 116.30, y: 39.60 }, { x: 37.50, y: 40.25 }, { x: 39.51, y: 36.25 }]
      function CreateBezierPoints(anchorpoints, pointsAmount) {
        var points = [];
        for (var i = 0; i < pointsAmount; i++) {
          var point = MultiPointBezier(anchorpoints, i / pointsAmount);
          points.push([point.x, point.y]);
        }
        return points;
      }
      function MultiPointBezier(points, t) {
        var len = points.length;
        var x = 0,
          y = 0;
        var erxiangshi = function (start, end) {
          var cs = 1,
            bcs = 1;
          while (end > 0) {
            cs *= start;
            bcs *= end;
            start--;
            end--;
          }
          return cs / bcs;
        };
        for (var i = 0; i < len; i++) {
          var point = points[i];
          x +=
            point.x *
            Math.pow(1 - t, len - 1 - i) *
            Math.pow(t, i) *
            erxiangshi(len - 1, i);
          y +=
            point.y *
            Math.pow(1 - t, len - 1 - i) *
            Math.pow(t, i) *
            erxiangshi(len - 1, i);
        }
        return { x: x, y: y };
      }
    },
    /**
     * 编辑飞线材质
     * @param {飞线效果} fragmentShaderSource
     */
    getFlylineMaterial(fragmentShaderSource) {
      // 创建材质，在MaterialAppearance中若不添加基础材质，模型将会透明
      var material = new Cesium.Material.fromType("Color");
      material.uniforms.color = Cesium.Color.ORANGE;
      // 自定义材质
      const aper = new Cesium.PolylineMaterialAppearance({
        material: material,
        translucent: true,
        vertexShaderSource: `
          #define CLIP_POLYLINE 
          void clipLineSegmentToNearPlane(
              vec3 p0,
              vec3 p1,
              out vec4 positionWC,
              out bool clipped,
              out bool culledByNearPlane,
              out vec4 clippedPositionEC)
          {
              culledByNearPlane = false;
              clipped = false;
              vec3 p0ToP1 = p1 - p0;
              float magnitude = length(p0ToP1);
              vec3 direction = normalize(p0ToP1);
              float endPoint0Distance =  czm_currentFrustum.x + p0.z;
              float denominator = -direction.z;
              if (endPoint0Distance > 0.0 && abs(denominator) < czm_epsilon7)
              {
                  culledByNearPlane = true;
              }
              else if (endPoint0Distance > 0.0)
              {
                  float t = endPoint0Distance / denominator;
                  if (t < 0.0 || t > magnitude)
                  {
                      culledByNearPlane = true;
                  }
                  else
                  {
                      p0 = p0 + t * direction;
                      p0.z = min(p0.z, -czm_currentFrustum.x);
                      clipped = true;
                  }
              }
              clippedPositionEC = vec4(p0, 1.0);
              positionWC = czm_eyeToWindowCoordinates(clippedPositionEC);
          }
          vec4 getPolylineWindowCoordinatesEC(vec4 positionEC, vec4 prevEC, vec4 nextEC, float expandDirection, float width, bool usePrevious, out float angle)
          {
              #ifdef POLYLINE_DASH
              vec4 positionWindow = czm_eyeToWindowCoordinates(positionEC);
              vec4 previousWindow = czm_eyeToWindowCoordinates(prevEC);
              vec4 nextWindow = czm_eyeToWindowCoordinates(nextEC);
              vec2 lineDir;
              if (usePrevious) {
                  lineDir = normalize(positionWindow.xy - previousWindow.xy);
              }
              else {
                  lineDir = normalize(nextWindow.xy - positionWindow.xy);
              }
              angle = atan(lineDir.x, lineDir.y) - 1.570796327;
              angle = floor(angle / czm_piOverFour + 0.5) * czm_piOverFour;
              #endif
              vec4 clippedPrevWC, clippedPrevEC;
              bool prevSegmentClipped, prevSegmentCulled;
              clipLineSegmentToNearPlane(prevEC.xyz, positionEC.xyz, clippedPrevWC, prevSegmentClipped, prevSegmentCulled, clippedPrevEC);
              vec4 clippedNextWC, clippedNextEC;
              bool nextSegmentClipped, nextSegmentCulled;
              clipLineSegmentToNearPlane(nextEC.xyz, positionEC.xyz, clippedNextWC, nextSegmentClipped, nextSegmentCulled, clippedNextEC);
              bool segmentClipped, segmentCulled;
              vec4 clippedPositionWC, clippedPositionEC;
              clipLineSegmentToNearPlane(positionEC.xyz, usePrevious ? prevEC.xyz : nextEC.xyz, clippedPositionWC, segmentClipped, segmentCulled, clippedPositionEC);
              if (segmentCulled)
              {
                  return vec4(0.0, 0.0, 0.0, 1.0);
              }
              vec2 directionToPrevWC = normalize(clippedPrevWC.xy - clippedPositionWC.xy);
              vec2 directionToNextWC = normalize(clippedNextWC.xy - clippedPositionWC.xy);
              if (prevSegmentCulled)
              {
                  directionToPrevWC = -directionToNextWC;
              }
              else if (nextSegmentCulled)
              {
                  directionToNextWC = -directionToPrevWC;
              }
              vec2 thisSegmentForwardWC, otherSegmentForwardWC;
              if (usePrevious)
              {
                  thisSegmentForwardWC = -directionToPrevWC;
                  otherSegmentForwardWC = directionToNextWC;
              }
              else
              {
                  thisSegmentForwardWC = directionToNextWC;
                  otherSegmentForwardWC =  -directionToPrevWC;
              }
              vec2 thisSegmentLeftWC = vec2(-thisSegmentForwardWC.y, thisSegmentForwardWC.x);
              vec2 leftWC = thisSegmentLeftWC;
              float expandWidth = width * 0.5;
              if (!czm_equalsEpsilon(prevEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1) && !czm_equalsEpsilon(nextEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1))
              {
                  vec2 otherSegmentLeftWC = vec2(-otherSegmentForwardWC.y, otherSegmentForwardWC.x);
                  vec2 leftSumWC = thisSegmentLeftWC + otherSegmentLeftWC;
                  float leftSumLength = length(leftSumWC);
                  leftWC = leftSumLength < czm_epsilon6 ? thisSegmentLeftWC : (leftSumWC / leftSumLength);
                  vec2 u = -thisSegmentForwardWC;
                  vec2 v = leftWC;
                  float sinAngle = abs(u.x * v.y - u.y * v.x);
                  expandWidth = clamp(expandWidth / sinAngle, 0.0, width * 2.0);
              }
              vec2 offset = leftWC * expandDirection * expandWidth * czm_pixelRatio;
              return vec4(clippedPositionWC.xy + offset, -clippedPositionWC.z, 1.0) * (czm_projection * clippedPositionEC).w;
          }
          vec4 getPolylineWindowCoordinates(vec4 position, vec4 previous, vec4 next, float expandDirection, float width, bool usePrevious, out float angle)
          {
              vec4 positionEC = czm_modelViewRelativeToEye * position;
              vec4 prevEC = czm_modelViewRelativeToEye * previous;
              vec4 nextEC = czm_modelViewRelativeToEye * next;
              return getPolylineWindowCoordinatesEC(positionEC, prevEC, nextEC, expandDirection, width, usePrevious, angle);
          }

          attribute vec3 position3DHigh;
          attribute vec3 position3DLow;
          attribute vec3 prevPosition3DHigh;
          attribute vec3 prevPosition3DLow;
          attribute vec3 nextPosition3DHigh;
          attribute vec3 nextPosition3DLow;
          attribute vec2 expandAndWidth;
          attribute vec2 st;
          attribute float batchId;

          varying float v_width;
          varying vec2 v_st;
          varying float v_polylineAngle;
          
          varying vec4 v_positionEC;
          varying vec3 v_normalEC;
          void main()
          {
          float expandDir = expandAndWidth.x;
          float width = abs(expandAndWidth.y) + 0.5;
          bool usePrev = expandAndWidth.y < 0.0;

          vec4 p = czm_computePosition();
          vec4 prev = czm_computePrevPosition();
          vec4 next = czm_computeNextPosition();
          
          float angle;
          vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, angle);
          gl_Position = czm_viewportOrthographic * positionWC;
          
          v_width = width;
          v_st.s = st.s;
          v_st.t = st.t;
          // v_st.t = czm_writeNonPerspective(st.t, gl_Position.w);
          v_polylineAngle = angle;


          
          vec4 eyePosition = czm_modelViewRelativeToEye * p;
          v_positionEC =  czm_inverseModelView * eyePosition;      // position in eye coordinates
          //v_normalEC = czm_normal * normal;                         // normal in eye coordinates
          }
      `,
        fragmentShaderSource: fragmentShaderSource,
      });
      return aper;
    },
    // 创建飞线对象
    addFlyline(viewer, data, index) {
      // 创建长方体对象
      const PolylineGeometry = new Cesium.PolylineGeometry({
        positions: this.computeFlyline(data.point1, data.point2, data.h),
        width: 4,
      });
      const instance = new Cesium.GeometryInstance({
        geometry: PolylineGeometry,
        id: "flyline-" + index,
      });
      let en = viewer.scene.primitives.add(
        new Cesium.Primitive({
          geometryInstances: [instance],
          appearance: this.getFlylineMaterial(data.fragmentShaderSource),
          releaseGeometryInstances: false,
          compressVertices: false,
        })
      );
    },
  },
};
</script>

<style lang="less" scoped>
</style>
